import CryptoJS, { MD5 } from 'crypto-js';

// 十六位十六进制数作为密钥
const SECRET_KEY = CryptoJS.enc.Utf8.parse('3333e6e143439161');
// 十六位十六进制数作为密钥偏移量
const SECRET_IV = CryptoJS.enc.Utf8.parse('e3bbe7e3ba84431a');

// 类型 window.localStorage,window.sessionStorage,
const config = {
  type: 'sessionStorage', // 本地存储类型 localStorage sessionStorage
  prefix: process.env.PUBLIC_PATH, // 名称前缀 建议：项目名 + 版本号
  // expire: 0, // 过期时间 单位：秒
  isEncrypt: process.env.NODE_ENV !== 'development', // 为了调试方便, 开发过程中可以不加密
};

/**
 * 加密方法
 * @param data
 * @returns {string}
 */
const encrypt = (data: any) => {
  if (typeof data === 'object') {
    try {
      data = JSON.stringify(data);
    } catch (error) {
      console.error('encrypt error:', error);
    }
  }
  const dataHex = CryptoJS.enc.Utf8.parse(data);
  const encrypted = CryptoJS.AES.encrypt(dataHex, SECRET_KEY, {
    iv: SECRET_IV,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  });
  return encrypted.ciphertext.toString();
};

/**
 * 解密方法
 * @param data
 * @returns {string}
 */
const decrypt = (data: any) => {
  const encryptedHexStr = CryptoJS.enc.Hex.parse(data);
  const str = CryptoJS.enc.Base64.stringify(encryptedHexStr);
  const decryptRes = CryptoJS.AES.decrypt(str, SECRET_KEY, {
    iv: SECRET_IV,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  });
  const decryptedStr = decryptRes.toString(CryptoJS.enc.Utf8);
  return decryptedStr.toString();
};

// 名称前自动添加前缀
const autoAddPrefix = (key: string) => {
  const prefix = config.prefix ? `${config.prefix}_` : '';
  return process.env.NODE_ENV === 'development' ? prefix + key : MD5(prefix + key).toString();
};

// 删除 removeStorage
export const removeStorage = (key: string) => {
  window[config.type].removeItem(autoAddPrefix(key));
};

// 清空 clearStorage
export const clearStorage = () => {
  window[config.type].clear();
};

// 判断是否支持 Storage
export const isSupportStorage = () => {
  return typeof Storage !== 'undefined';
};

// 获取全部 getAllStorage
export const getStorageAll = () => {
  let len = window[config.type].length; // 获取长度
  let arr = new Array(); // 定义数据集
  for (let i = 0; i < len; i++) {
    // 获取key 索引从0开始
    let getKey = window[config.type].key(i);
    // 获取key对应的值
    let getVal = window[config.type].getItem(getKey);
    // 放进数组
    arr[i] = { key: getKey, val: getVal };
  }
  return arr;
};

// 是否存在 hasStorage
export const hasStorage = (key: string) => {
  key = autoAddPrefix(key);
  let arr = getStorageAll().filter((item) => {
    return item.key === key;
  });
  return !!arr.length;
};

// 获取所有key
export const getStorageKeys = () => {
  let items = getStorageAll();
  let keys = [];
  for (let index = 0; index < items.length; index++) {
    keys.push(items[index].key);
  }
  return keys;
};

// 根据索引获取key
export const getStorageForIndex = (index: number) => {
  return window[config.type].key(index);
};

// 获取localStorage长度
export const getStorageLength = () => {
  return window[config.type].length;
};

/**
 * @param key 存储字段key
 * @param value 存储内容
 * @param expire 过期时间
 */
export const setStorage = (key: string, value: any, expire = 0) => {
  if (value === '' || value === null || value === undefined) {
    value = null;
  }

  if (Number.isNaN(expire) || expire < 0) {
    throw new Error('Expire must be a number');
  }

  expire *= 1000;
  let data = {
    value, // 存储值
    time: Date.now(), // 存值时间戳
    expire, // 过期时间
  };

  const encryptString = config.isEncrypt ? encrypt(JSON.stringify(data)) : JSON.stringify(data);

  window[config.type].setItem(autoAddPrefix(key), encryptString);
};

// 获取 getStorage
export const getStorage = (key: string) => {
  key = autoAddPrefix(key);
  // key 不存在判断
  if (
    !window[config.type].getItem(key) ||
    JSON.stringify(window[config.type].getItem(key)) === 'null'
  ) {
    return null;
  }

  try {
    // 优化 持续使用中续期
    const storage = config.isEncrypt
      ? JSON.parse(decrypt(window[config.type].getItem(key)))
      : JSON.parse(window[config.type].getItem(key));

    if (storage.value) {
      let nowTime = Date.now();
      // 过期删除
      if (storage.expire < nowTime - storage.time && storage.expire !== 0) {
        removeStorage(key);
        return null;
      }
      return storage.value;
    }
    return storage;
  } catch (error) {
    // 有可能是第三库写入的存储，格式不一致，而我们又想获取这个值，直接原数据返回
    return window[config.type].getItem(key);
  }
};
